package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import javafx.scene.input.TouchEvent;
import org.apache.http.HttpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @author ：Shine
 * @description：
 * @date ：2021/2/25 18:25
 */
@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    //从配置文件中获取url
    @Value("${authUrls.url}")
    private String authUrlsUrl;

    //这是一个路径匹配的工具类
    AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取到当前的url解析出路径，判断当前的路径是否是我们想要拦截的路径
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        if (antPathMatcher.match("/**/inner/**", path)) {
            //符合匹配规则，作出响应
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //判断用户是否登录
        String userId = this.getUserId(request);
        //判断用户是否登录
        String userTempId = this.getUserTempId(request);
        if ("-1".equals(userId)) {
            //    用户未登录，作出响应
            ServerHttpResponse response = exchange.getResponse();
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //判断在未登录时是否访问了带有auth的控制器
        if (antPathMatcher.match("/api/**/auth/**", path)) {

            if (StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //判断用户是否访问了黑白名单
        String[] splits = authUrlsUrl.split(",");
        if (splits != null && splits.length > 0) {
            for (String url : splits) {
                //    如果用户访问了黑名单中的任何一个路径，且在未登录的情况下，将提示用户进行登录
                // 该方法如果包含url的话，就返回下标，否则返回-1
                if (path.indexOf(url) != -1 && StringUtils.isEmpty(userId)) {
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.SEE_OTHER);//固定的从定向状态码
                    response.getHeaders().set(HttpHeaders.LOCATION, "http://www.gmall.com/login.html?originUrl=" + request.getURI());
                    return response.setComplete();//该方法是重定向
                }
            }
        }

        //如果上述操作全都通过的话，将userId,userTempId传递到各个微服务中
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)) {
            if (!StringUtils.isEmpty(userId)) {
                request.mutate().header("userId", userId).build();
            }

            if (!StringUtils.isEmpty(userTempId)) {
                request.mutate().header("userTempId", userTempId).build();
            }
            //将request转换为exchange对象
            return chain.filter(exchange.mutate().request(request).build());
        }

        return chain.filter(exchange);
    }

    /**
     * 从cookie中获取临时用户id
     *
     * @param request
     * @return
     */
    private String getUserTempId(ServerHttpRequest request) {
        String userTempId = "";

        List<String> tempIdList = request.getHeaders().get("userTempId");
        if (!StringUtils.isEmpty(tempIdList)) {
            String tempId = tempIdList.get(0);
        } else {
            HttpCookie cookie = request.getCookies().getFirst("userTempId");
            if (cookie != null) {
                userTempId = cookie.getValue();
            }
        }

        return userTempId;
    }

    /**
     * 输出数据到页面
     *
     * @param response
     * @param resultCode
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCode) {
        Result<Object> build = Result.build(null, resultCode);
        String alert = JSONObject.toJSONString(build);

        DataBuffer alertByte = response.bufferFactory().wrap(alert.getBytes());

        //设置响应头格式编码
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        return response.writeWith(Mono.just(alertByte));
    }


    private String getUserId(ServerHttpRequest request) {
        //如果header中有，从header中获取，如果没有从cookie中拿
        String token = "";

        List<String> list = request.getHeaders().get("token");
        if (list != null) {
            token = list.get(0);
        } else {
            HttpCookie cookie = request.getCookies().getFirst("token");
            if (cookie != null) {
                token = cookie.getValue();
            }
        }

        //判断是否拿到了token
        if (!StringUtils.isEmpty(token)) {
            String userKey = "user:login:" + token;
            String userJson = (String) redisTemplate.opsForValue().get(userKey);

            JSONObject userJsonObject = JSONObject.parseObject(userJson);
            String ip = userJsonObject.getString("ip");

            //比较当前登录的IP地址是否和缓存中的IP地址相同
            String gatwayIp = IpUtil.getGatwayIpAddress(request);
            if (ip.equals(gatwayIp)) {
                //    获取用户id
                String userId = userJsonObject.getString("userId");
                return userId;
            } else {
                return "-1";
            }
        }
        return null;
    }
}
